/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.nio.channels;

import java.io.IOException;
import java.net.Socket;
import java.net.SocketOption;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.SelectorProvider;

/**
 * 面向流的连接套接字的可选通道。
 *
 * <p> 套接字通道是通过调用该类的open方法之一来创建的。
 * 为任意预先存在的套接字创建通道是不可能的。新创建的套接字通道已打开，但尚未连接。
 * 尝试在未连接的通道上调用I/O操作将导致抛出NotYetConnectedException。
 * 可以通过调用其connect方法来连接套接字通道;
 * 一旦连接，套接字通道将保持连接，直到关闭。
 * 套接字通道是否连接可以通过调用它的isConnected方法来确定。
 *
 * <p> 套接字通道支持非阻塞连接:
 * 可以创建一个套接字通道，
 * 建立远程套接字链接的过程可以通过connect方法发起，稍后由finishConnect方法完成。
 * 连接操作是否正在进行可以通过调用isConnectionPending方法来确定。
 *
 * <p> 套接字通道支持异步关闭，这类似于Channel类中指定的异步关闭操作。
 * 如果一个线程关闭了套接字的输入端，而另一个线程在套接字的通道上阻塞了读操作，
 * 那么阻塞线程中的read操作将在不读取任何字节的情况下完成，并返回-1。
 * 如果套接字的输出端被一个线程关闭，而另一个线程在套接字的通道上的写操作中被阻塞，
 * 那么被阻塞的线程将收到一个AsynchronousCloseException。
 *
 * <p> 套接字选项使用setOption方法进行配置。套接字通道支持以下选项:
 * <blockquote>
 * <table border summary="Socket options">
 *   <tr>
 *     <th>Option Name</th>
 *     <th>Description</th>
 *   </tr>
 *   <tr>
 *     <td> {@link java.net.StandardSocketOptions#SO_SNDBUF SO_SNDBUF} </td>
 *     <td> 发送缓冲区的大小 </td>
 *   </tr>
 *   <tr>
 *     <td> {@link java.net.StandardSocketOptions#SO_RCVBUF SO_RCVBUF} </td>
 *     <td> 接收缓冲区的大小 </td>
 *   </tr>
 *   <tr>
 *     <td> {@link java.net.StandardSocketOptions#SO_KEEPALIVE SO_KEEPALIVE} </td>
 *     <td> 保持连接处于连接状态 </td>
 *   </tr>
 *   <tr>
 *     <td> {@link java.net.StandardSocketOptions#SO_REUSEADDR SO_REUSEADDR} </td>
 *     <td> 重用地址 </td>
 *   </tr>
 *   <tr>
 *     <td> {@link java.net.StandardSocketOptions#SO_LINGER SO_LINGER} </td>
 *     <td> 在关闭时，如果数据存在(当配置为阻塞模式时) </td>
 *   </tr>
 *   <tr>
 *     <td> {@link java.net.StandardSocketOptions#TCP_NODELAY TCP_NODELAY} </td>
 *     <td> 禁用Nagle算法 </td>
 *   </tr>
 * </table>
 * </blockquote>
 * 还可能支持其他(特定于实现的)选项。
 *
 * <p> 套接字通道对于多个并发线程来说是安全的。
 * 它们支持并发读写，尽管在任何给定的时间内最多有一个线程在读，最多有一个线程在写。
 * connect和finishConnect方法是相互同步的，当其中一个方法的调用正在进行时，尝试发起读写操作将会阻塞，直到调用完成。</p>
 *
 * @author Mark Reinhold
 * @author JSR-51 Expert Group
 * @since 1.4
 */

public abstract class SocketChannel
    extends AbstractSelectableChannel
    implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel
{

    /**
     * 创建实例
     *
     * @param  provider
     *         The provider that created this channel
     */
    protected SocketChannel(SelectorProvider provider) {
        super(provider);
    }

    /**
     * 打开套接字通道。
     *
     * <p> 新通道是通过调用系统范围默认java.nio.channels.spi的SelectorProvider对象的openSocketChannel方法创建的。</p>
     *
     * @return  A new socket channel
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    public static SocketChannel open() throws IOException {
        return SelectorProvider.provider().openSocketChannel();
    }

    /**
     * 打开一个套接字通道并将其连接到一个远程地址。
     *
     * <p> 这个方便的方法工作起来就像调用open()方法，在产生的套接字通道上调用connect方法，将其远程传递，然后返回该通道。 </p>
     *
     * @param  remote
     *         The remote address to which the new channel is to be connected
     *
     * @return  A new, and connected, socket channel
     *
     * @throws  AsynchronousCloseException
     *          If another thread closes this channel
     *          while the connect operation is in progress
     *
     * @throws  ClosedByInterruptException
     *          If another thread interrupts the current thread
     *          while the connect operation is in progress, thereby
     *          closing the channel and setting the current thread's
     *          interrupt status
     *
     * @throws  UnresolvedAddressException
     *          If the given remote address is not fully resolved
     *
     * @throws  UnsupportedAddressTypeException
     *          If the type of the given remote address is not supported
     *
     * @throws  SecurityException
     *          If a security manager has been installed
     *          and it does not permit access to the given remote endpoint
     *
     * @throws  IOException
     *          If some other I/O error occurs
     */
    public static SocketChannel open(SocketAddress remote)
        throws IOException
    {
        SocketChannel sc = open();
        try {
            sc.connect(remote);
        } catch (Throwable x) {
            try {
                sc.close();
            } catch (Throwable suppressed) {
                x.addSuppressed(suppressed);
            }
            throw x;
        }
        assert sc.isConnected();
        return sc;
    }

    /**
     * 返回一个操作集，标识此通道支持的操作。
     *
     * <p> 套接字通道支持连接、读取和写入，
     * 因此该方法返回(SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE)。</p>
     *
     * @return  The valid-operation set
     */
    public final int validOps() {
        return (SelectionKey.OP_READ
                | SelectionKey.OP_WRITE
                | SelectionKey.OP_CONNECT);
    }


    // -- Socket-specific operations --

    /**
     * @throws  ConnectionPendingException
     *          If a non-blocking connect operation is already in progress on
     *          this channel
     * @throws  AlreadyBoundException               {@inheritDoc}
     * @throws  UnsupportedAddressTypeException     {@inheritDoc}
     * @throws  ClosedChannelException              {@inheritDoc}
     * @throws  IOException                         {@inheritDoc}
     * @throws  SecurityException
     *          If a security manager has been installed and its
     *          {@link SecurityManager#checkListen checkListen} method denies
     *          the operation
     *
     * @since 1.7
     */
    @Override
    public abstract SocketChannel bind(SocketAddress local)
        throws IOException;

    /**
     * @throws  UnsupportedOperationException           {@inheritDoc}
     * @throws  IllegalArgumentException                {@inheritDoc}
     * @throws  ClosedChannelException                  {@inheritDoc}
     * @throws  IOException                             {@inheritDoc}
     *
     * @since 1.7
     */
    @Override
    public abstract <T> SocketChannel setOption(SocketOption<T> name, T value)
        throws IOException;

    /**
     * 在不关闭通道的情况下关闭读取连接。
     *
     * <p> 一旦停止读取，则在通道上进一步读取将返回-1，即流结束指示。
     * 如果连接的输入端已经关闭，则调用此方法没有效果。
     *
     * @return  The channel
     *
     * @throws  NotYetConnectedException
     *          If this channel is not yet connected
     * @throws  ClosedChannelException
     *          If this channel is closed
     * @throws  IOException
     *          If some other I/O error occurs
     *
     * @since 1.7
     */
    public abstract SocketChannel shutdownInput() throws IOException;

    /**
     * 在不关闭通道的情况下关闭写入连接。
     *
     * <p> 一旦因为写入而关闭，那么进一步尝试写入通道将抛出ClosedChannelException。
     * 如果连接的输出端已经关闭，那么调用这个方法没有效果。
     *
     * @return  The channel
     *
     * @throws  NotYetConnectedException
     *          If this channel is not yet connected
     * @throws  ClosedChannelException
     *          If this channel is closed
     * @throws  IOException
     *          If some other I/O error occurs
     *
     * @since 1.7
     */
    public abstract SocketChannel shutdownOutput() throws IOException;

    /**
     * 检索与此通道关联的套接字。
     *
     * <p> 返回的对象不会声明没有在java.net.Socket类中声明的任何公共方法。 </p>
     *
     * @return  A socket associated with this channel
     */
    public abstract Socket socket();

    /**
     * 判断此通道的网络套接字是否已连接。
     *
     * @return  <tt>true</tt> if, and only if, this channel's network socket
     *          is {@link #isOpen open} and connected
     */
    public abstract boolean isConnected();

    /**
     * 说明该通道上是否有正在进行的连接操作。
     *
     * @return  <tt>true</tt> if, and only if, a connection operation has been
     *          initiated on this channel but not yet completed by invoking the
     *          {@link #finishConnect finishConnect} method
     */
    public abstract boolean isConnectionPending();

    /**
     * 连接此通道的套接字。
     *
     * <p> 如果此通道处于非阻塞模式，则此方法的调用将启动非阻塞连接操作。
     * 如果连接立即建立，就像本地连接一样，那么这个方法将返回true。
     * 否则，该方法将返回false，并且连接操作必须在稍后通过调用finishConnect方法来完成。
     *
     * <p> 如果此通道处于阻塞模式，则此方法的调用将阻塞，直到建立连接或发生I/O错误。
     *
     * <p> 此方法执行与java.net.Socket类完全相同的安全检查。
     * 也就是说，如果已经安装了安全管理器，那么该方法将验证其checkConnect方法是否允许连接到给定远程端点的地址和端口号。
     *
     * <p> 这个方法可以在任何时候被调用。如果在此通道上的读或写操作被调用时，
     * 该方法的调用正在进行中，那么该操作将首先阻塞，直到该调用完成。
     * 如果发起连接尝试但失败，也就是说，如果此方法的调用抛出一个检查过的异常，则通道将被关闭。</p>
     *
     * @param  remote
     *         The remote address to which this channel is to be connected
     *
     * @return  <tt>true</tt> if a connection was established,
     *          <tt>false</tt> if this channel is in non-blocking mode
     *          and the connection operation is in progress
     *
     * @throws  AlreadyConnectedException
     *          If this channel is already connected
     *
     * @throws  ConnectionPendingException
     *          If a non-blocking connection operation is already in progress
     *          on this channel
     *
     * @throws  ClosedChannelException
     *          If this channel is closed
     *
     * @throws  AsynchronousCloseException
     *          If another thread closes this channel
     *          while the connect operation is in progress
     *
     * @throws  ClosedByInterruptException
     *          If another thread interrupts the current thread
     *          while the connect operation is in progress, thereby
     *          closing the channel and setting the current thread's
     *          interrupt status
     *
     * @throws  UnresolvedAddressException
     *          If the given remote address is not fully resolved
     *
     * @throws  UnsupportedAddressTypeException
     *          If the type of the given remote address is not supported
     *
     * @throws  SecurityException
     *          If a security manager has been installed
     *          and it does not permit access to the given remote endpoint
     *
     * @throws  IOException
     *          If some other I/O error occurs
     */
    public abstract boolean connect(SocketAddress remote) throws IOException;

    /**
     * 完成连接socket通道的过程。
     *
     * <p> 通过将socket通道置于非阻塞模式，然后调用其connect方法，可以启动非阻塞连接操作。
     * 一旦建立了连接，或者尝试失败，套接字通道将变为可连接的，并且可能会调用此方法来完成连接序列。
     * 如果connection操作失败，则调用此方法将要抛出适当的java.io.IOException。
     *
     * <p> 如果此通道已经连接，则此方法不会阻塞，并将立即返回true。
     * 如果这个通道是非阻塞模式，那么如果连接过程还没有完成，这个方法将返回false。
     * 如果此通道处于blocking模式，则此方法将阻塞，
     * 直到连接完成或失败，并总是返回true或抛出描述失败的checked exception。
     *
     * <p> 这个方法可以在任何时候被调用。如果在此通道上的读或写操作被调用时，
     * 该方法的调用正在进行中，那么该操作将首先阻塞，直到该调用完成。
     * 如果连接尝试失败，也就是说，如果这个方法的调用抛出一个检查过的异常，那么通道将被关闭。</p>
     *
     * @return  <tt>true</tt> if, and only if, this channel's socket is now
     *          connected
     *
     * @throws  NoConnectionPendingException
     *          If this channel is not connected and a connection operation
     *          has not been initiated
     *
     * @throws  ClosedChannelException
     *          If this channel is closed
     *
     * @throws  AsynchronousCloseException
     *          If another thread closes this channel
     *          while the connect operation is in progress
     *
     * @throws  ClosedByInterruptException
     *          If another thread interrupts the current thread
     *          while the connect operation is in progress, thereby
     *          closing the channel and setting the current thread's
     *          interrupt status
     *
     * @throws  IOException
     *          If some other I/O error occurs
     */
    public abstract boolean finishConnect() throws IOException;

    /**
     * 返回此通道套接字连接到的远程地址。
     *
     * <p> 如果通道被绑定并连接到Internet Protocol 套接字地址，那么该方法的返回值类型为java.net.InetSocketAddress。
     *
     * @return  The remote address; {@code null} if the channel's socket is not
     *          connected
     *
     * @throws  ClosedChannelException
     *          If the channel is closed
     * @throws  IOException
     *          If an I/O error occurs
     *
     * @since 1.7
     */
    public abstract SocketAddress getRemoteAddress() throws IOException;

    // -- ByteChannel operations --

    /**
     * @throws  NotYetConnectedException
     *          If this channel is not yet connected
     */
    public abstract int read(ByteBuffer dst) throws IOException;

    /**
     * @throws  NotYetConnectedException
     *          If this channel is not yet connected
     */
    public abstract long read(ByteBuffer[] dsts, int offset, int length)
        throws IOException;

    /**
     * @throws  NotYetConnectedException
     *          If this channel is not yet connected
     */
    public final long read(ByteBuffer[] dsts) throws IOException {
        return read(dsts, 0, dsts.length);
    }

    /**
     * @throws  NotYetConnectedException
     *          If this channel is not yet connected
     */
    public abstract int write(ByteBuffer src) throws IOException;

    /**
     * @throws  NotYetConnectedException
     *          If this channel is not yet connected
     */
    public abstract long write(ByteBuffer[] srcs, int offset, int length)
        throws IOException;

    /**
     * @throws  NotYetConnectedException
     *          If this channel is not yet connected
     */
    public final long write(ByteBuffer[] srcs) throws IOException {
        return write(srcs, 0, srcs.length);
    }

    /**
     * {@inheritDoc}
     * <p>
     * 如果设置了安全管理器，则使用本地地址和-1作为参数调用其checkConnect方法，以查看是否允许该操作。
     * 如果不允许该操作，则返回一个表示环回地址和通道套接字的本地端口的SocketAddress。
     *
     * @return  The {@code SocketAddress} that the socket is bound to, or the
     *          {@code SocketAddress} representing the loopback address if
     *          denied by the security manager, or {@code null} if the
     *          channel's socket is not bound
     *
     * @throws  ClosedChannelException     {@inheritDoc}
     * @throws  IOException                {@inheritDoc}
     */
    @Override
    public abstract SocketAddress getLocalAddress() throws IOException;

}
